package main

import (
	"errors"
	"fmt"
	//	"image/png"
	//"os"
	"strings"
	"time"

	"gitee.com/liukexing/isrn/model"
	"gitee.com/liukexing/isrn/router"

	"github.com/orcaman/concurrent-map"

	"gitee.com/liukexing/isrn/app/service/controller"
	//	appm "gitee.com/liukexing/isrn/app/service/model"
	//"github.com/go-vgo/robotgo"
)

const ()

var (
	exit    chan int
	nodes   cmap.ConcurrentMap
	reqmaps cmap.ConcurrentMap
)

func main() {
	nodes = cmap.New()
	reqmaps = cmap.New()
	controller.SetNodes(&nodes, &reqmaps)
	exit = make(chan int, 1)
	if err := router.StartRouter("D:/ipfs_repo/repo_g2", 10041); err != nil {
		fmt.Println("Init failed:", err)
		return
	}
	router.TranAction = TranReceiver
	router.BoradcastAction = BoradcastReceiver
	time.Sleep(5 * time.Second)
	router.BoradcastAllConn("QmWikJF888ABLBeCAtR2SQA4sHMd4wU9ucLcHhZo4hutmX")
	<-exit
	return
}

func TestResponse(data *model.TranInfo, sender string) error {
	if data.Action == "error" {
		fmt.Println("TestResponse error")
		return errors.New(string(data.Data))
	}
	info, _ := reqmaps.Get(string(data.ReqNum))
	temp := info.(*model.ResponseInfo)
	fmt.Println(string(data.ReqNum), time.Since(temp.ReqTime))
	fmt.Println(len(data.Data), string(data.Data), reqmaps.Count())
	//	shots := &appm.ScreenShots{}
	//	_, err := shots.GetData(data.Data)
	//	if err != nil {
	//		return err
	//	}
	//	v := shots.Image

	//	fileName := fmt.Sprintf("%dx%d_%s.png", v.Rect.Dx(), v.Rect.Dy(), data.ReqNum)
	//	fmt.Println(fileName)
	//	file, _ := os.Create(fileName)
	//	defer file.Close()
	//	png.Encode(file, &v)
	return nil
}

func KeySwapResponse(data *model.TranInfo, sender string) error {
	if data.Action == "error" {
		return errors.New(string(data.Data))
	}
	info, ok := nodes.Get(sender)
	if !ok {
		return errors.New("Node Info Not Find")
	}
	nodeinfo := info.(*router.NodeInfo)
	key := &nodeinfo.Key

	swapinfo := &model.KeySwapInfo{}
	_, err := swapinfo.GetData(data.Data)
	if err != nil {
		return err
	}

	pk, err := key.UnmarshalPk(swapinfo.Pk)
	if err != nil {
		return err
	}
	cn, err := pk.Verify(data.Data, data.Sign)
	if err != nil {
		return err
	}
	if !cn {
		return errors.New("Verify Check failed")
	}
	rpk, err := key.UnmarshalRpk(swapinfo.Rpk)
	if err != nil {
		return err
	}
	key.Pk = pk
	key.Rpk = rpk
	//MsgSend(sender, "sys/GetShot", []byte(fmt.Sprint("hello ")), false, true, TestResponse)
	MsgSend(sender, "test/hello", []byte(fmt.Sprint("hello world")), false, true, TestResponse)

	return nil
}

func MsgSend(receiver, action string, data []byte, encrypt, signcheck bool, resfun model.ResponseHandle) error {
	info, ok := nodes.Get(receiver)
	if !ok {
		return errors.New("Node Info Not Find")
	}
	nodeinfo := info.(*router.NodeInfo)
	if len(nodeinfo.RouList) == 0 {
		return errors.New("No Router Path Find")
	}
	sn, err := nodeinfo.Key.Sign(data)
	if err != nil {
		return err
	}
	tranMsg := &model.TranInfo{}
	tranMsg.Action = action
	tranMsg.Data = data
	tranMsg.Encrypt = encrypt
	tranMsg.ReqNum = []byte(model.GetRandomString(12))
	tranMsg.Sign = sn

	if encrypt {
		endata, err := nodeinfo.Key.Encrypt(data)
		if err != nil {
			return err
		}
		tranMsg.Data = endata
	}

	err = router.TransSend(receiver, strings.Split(nodeinfo.RouList[0].Path, ","), tranMsg)
	if err != nil {
		return err
	}
	reqmaps.Set(string(tranMsg.ReqNum), &model.ResponseInfo{
		Receiver:  receiver,
		Handle:    resfun,
		SignCheck: signcheck,
		ReqTime:   time.Now(),
	})
	return nil
}

func TranReceiver(data *model.Trans) error {
	traninfo := &model.TranInfo{}
	if _, err := traninfo.GetData(data.Data); err != nil {
		return err
	}

	if traninfo.Action == "router/back" {
		nodes.Upsert(data.PackInfo.Sender, &router.NodeInfo{
			Peerid: data.PackInfo.Sender,
			ReqId:  string(traninfo.Sign),
			RouList: []router.PathInfo{
				{
					Path:   string(traninfo.Data),
					Length: len(strings.Split(string(traninfo.Data), ",")),
					Update: time.Now(),
				},
			},
			Create: time.Now(),
		}, updateNodeInfo)
		//test action invoke
		rc := &router.RemoteCyprto{}
		swap, err := rc.BuildSwapInfo("Request")
		if err != nil {
			return err
		}
		swapinfo, err := swap.GetByte()
		if err != nil {
			return err
		}
		return MsgSend(data.PackInfo.Sender, "keyswap", swapinfo, false, false, KeySwapResponse)
	} else {
		if info, ok := nodes.Get(data.PackInfo.Sender); ok {
			go controller.Action(traninfo, info.(*router.NodeInfo))
		}
	}
	return nil
}
func updateNodeInfo(exist bool, oldValue interface{}, newValue interface{}) interface{} {
	if exist {
		oldnode := oldValue.(*router.NodeInfo)
		newnode := newValue.(*router.NodeInfo)
		if oldnode.ReqId == newnode.ReqId {
			oldnode.RouList = append(oldnode.RouList, newnode.RouList[0])
			return oldnode
		} else {
			return newnode
		}
	} else {
		return newValue
	}
}

func BoradcastReceiver(data *model.Trans) error {
	boradcastinfo := &model.BoradcastInfo{}
	if _, err := boradcastinfo.GetData(data.Data); err != nil {
		return err
	}

	//Save Node info
	nodes.Upsert(boradcastinfo.NodeId, &router.NodeInfo{
		Peerid: boradcastinfo.NodeId,
		ReqId:  boradcastinfo.Sign,
		RouList: []router.PathInfo{
			{
				Path:   strings.Join(data.PackInfo.Trans, ","),
				Length: len(data.PackInfo.Trans),
				Update: time.Now(),
			},
		},
		Create: time.Now(),
	}, updateNodeInfo)

	//response routepath
	//Trans reverse
	routerpath := make([]string, len(data.PackInfo.Trans))
	for k, v := range data.PackInfo.Trans {
		routerpath[(len(data.PackInfo.Trans)-1)-k] = v
	}
	traninfo := &model.TranInfo{}
	traninfo.Action = "router/back"
	traninfo.Data = []byte(strings.Join(routerpath, ","))
	traninfo.Encrypt = false
	traninfo.ReqNum = []byte(model.GetRandomString(12))
	traninfo.Sign = []byte(boradcastinfo.Sign)
	return router.TransSend(boradcastinfo.NodeId, data.PackInfo.Trans, traninfo)
}
